﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Linq;
using System.Reflection;
using System.ComponentModel;
using System.Diagnostics;

namespace OpenQuarters.EntityBase
{
    [Serializable]
    public abstract class EntityBase<TEntityType, TKeyType, TContextType>
        where TEntityType : EntityBase<TEntityType, TKeyType, TContextType>
        where TContextType : DataContext
    {
        public static RepositoryBase<TEntityType, TKeyType, TContextType> Repository
        {
            get
            {
                return OpenQuarters.EntityBase.Utils.ContextItem.GetItem("OpenQuarters.EntityBase.RepositoryBase<" + typeof(TEntityType).FullName + ", " + typeof(TKeyType).FullName + ", " + typeof(TContextType).FullName + ">.Repository", () => new RepositoryBase<TEntityType, TKeyType, TContextType>.EntityRepository());
            }
        }

        public abstract TKeyType GetKey(TEntityType Entity);
        public abstract System.Linq.Expressions.Expression<Func<TEntityType, bool>> GetIDSelector(TKeyType ID);
        public virtual DataLoadOptions GetLoadOptions()
        {
            return new DataLoadOptions();
        }

        public virtual bool IsNew(TEntityType Entity)
        {
            if (typeof(TKeyType) == typeof(long) ||
                typeof(TKeyType) == typeof(int) ||
                typeof(TKeyType) == typeof(byte) ||
                typeof(TKeyType) == typeof(short) ||
                typeof(TKeyType) == typeof(ushort) ||
                typeof(TKeyType) == typeof(uint))
            {
                return (Convert.ToInt64(Entity.GetKey(Entity)) <= 0);
            }
            else if (typeof(TKeyType) == typeof(string))
            {
                TKeyType key = Entity.GetKey(Entity);
                return (key == null || (key as string).Length == 0);
            }
            else if (typeof(TKeyType) == typeof(ulong))
            {
                return (Convert.ToUInt64(Entity.GetKey(Entity)) <= 0);
            }
            throw new NotImplementedException();
        }

        public bool IsNew()
        {
            return IsNew(this as TEntityType);
        }

        public EntityBase()
        {
            OnCreated();
        }

        public static TEntityType Load(TKeyType ID)
        {
            return Repository.Load(ID);
        }

        public virtual void OnCreated()
        {
        }

        public virtual void OnSaving()
        {
        }

        public virtual void OnDeleting()
        {
        }

        public virtual void OnLoading()
        {
        }

        public virtual void OnSaved()
        {
        }

        public virtual void OnDeleted()
        {
        }

        public virtual void OnLoaded()
        {
        }

        public virtual TEntityType Save()
        {
            return Save(this as TEntityType);
        }

        public TEntityType SaveRecursively()
        {
            return SaveRecursively(this as TEntityType);
        }

        public static TEntityType Save(TEntityType Entity)
        {
            Entity.OnSaving();
            Entity = Repository.Save(Entity);
            Entity = Load(Entity.GetKey(Entity));
            Entity.OnSaved();
            return Entity;
        }

        public static TEntityType SaveRecursively(TEntityType Entity)
        {
            Entity.OnSaving();
            Entity = Repository.SaveRecursively(Entity);
            Entity = Load(Entity.GetKey(Entity));
            Entity.OnSaved();
            return Entity;
        }

        public TEntityType Delete()
        {
            return Delete(this as TEntityType);
        }

        public TEntityType DeleteRecursively()
        {
            return DeleteRecursively(this as TEntityType);
        }

        public static TEntityType Delete(TEntityType Entity)
        {
            Entity.OnDeleting();
            Entity = Repository.Delete(Entity);
            Entity.OnDeleted();
            return Entity;
        }

        public static TEntityType DeleteRecursively(TEntityType Entity)
        {
            Entity.OnDeleting();
            Entity = Repository.DeleteRecursively(Entity);
            Entity.OnDeleted();
            return Entity;
        }

        public TEntityType Detach()
        {
            return OpenQuarters.EntityBase.EntityDetacher<TEntityType>.Detach(this as TEntityType);
        }

        private static TEntityType _sampleInstance = null;

        public static TContextType DataContext
        {
            get
            {
                if (_sampleInstance == null)
                {
                    _sampleInstance = Activator.CreateInstance<TEntityType>();
                }
                var r = DataContextHelper.GetDataContext<TContextType>();
                r.LoadOptions = _sampleInstance.GetLoadOptions();
                r.Log = Console.Out;
                return r;
            }
        }

        public static Table<TEntityType> Table
        {
            get
            {
                return DataContext.GetTable<TEntityType>();
            }
        }
    }
}
